#include "modbus_led.hpp"
#include <iomanip>

#ifndef MODBUS_LED_USE_CONFIG_H
#define MODBUS_LED_NAME ("modbus_led")

#define MODBUS_LED_RS485_PATH ("/dev/ttyUSB0")
#define MODBUS_LED_RS485_BAUDRATE (9600)
#define MODBUS_LED_RS485_DE_PATH ("/dev/gpiochip3")
#define MODBUS_LED_RS485_DE_PIN (24)
#define MODBUS_LED_RS485_FRAME_TIMEOUT_MS (500)

#define MODBUS_LED_MODBUS_SLAVE_ADDR (0x01)
#endif // MODBUS_LED_USE_CONFIG_H

uart_v2 *Pmodbus_led_uart = NULL;
rs485_v3 *Pmodbus_led_rs485 = NULL;
agile_modbus_rtu_master_simple *Pmodbus_led_rtu_master = NULL;

yj_return init_flag = YJ_ERROR;

int modbus_led_rs485_modbus_read(uint8_t *data)
{
    if (!Pmodbus_led_rs485 || !Pmodbus_led_rtu_master || !Pmodbus_led_uart || !data)
        return -1;
    return Pmodbus_led_rs485->rs485_recv(data, MAX_ADU_LENGTH);
}

int modbus_led_rs485_modbus_write(uint8_t *data, uint32_t len)
{
    if (!Pmodbus_led_rs485 || !Pmodbus_led_rtu_master || !Pmodbus_led_uart || !data)
        return -1;
    return Pmodbus_led_rs485->rs485_send(data, len);
}

yj_return modbus_led_init(void)
{
    std::string name(MODBUS_LED_NAME);
    Pmodbus_led_uart = new uart_v2(MODBUS_LED_RS485_PATH, MODBUS_LED_RS485_BAUDRATE, 8, 1);
    // Pmodbus_led_rs485 = new rs485_v3(Pmodbus_led_uart, MODBUS_LED_RS485_DE_PATH, MODBUS_LED_RS485_DE_PIN, MODBUS_LED_RS485_FRAME_TIMEOUT_MS);
    Pmodbus_led_rs485 = new rs485_v3(Pmodbus_led_uart, MODBUS_LED_RS485_FRAME_TIMEOUT_MS);
    Pmodbus_led_rtu_master = new agile_modbus_rtu_master_simple(name,
                                                                modbus_led_rs485_modbus_write,
                                                                modbus_led_rs485_modbus_read);

    if (Pmodbus_led_rs485->rs485_open() != YJ_OK)
        return init_flag;
    if (Pmodbus_led_rtu_master->init() != 0)
        return init_flag;
    init_flag = YJ_OK;
    return init_flag;
}

void modbus_led_deinit(void)
{
    if (Pmodbus_led_rs485)
    {
        Pmodbus_led_rs485->rs485_close();
        delete Pmodbus_led_rs485;
        Pmodbus_led_rs485 = NULL;
    }
    if (Pmodbus_led_rtu_master)
    {
        delete Pmodbus_led_rtu_master;
        Pmodbus_led_rtu_master = NULL;
    }
    if (Pmodbus_led_uart)
    {
        delete Pmodbus_led_uart;
        Pmodbus_led_uart = NULL;
    }
}

yj_return modbus_led_find()
{
    modbus_led_data_t data;
    uint8_t addr = 0x01;
    std::vector<uint16_t> dataTorecv(2);

    if (init_flag == YJ_ERROR)
    {
        LOG(ERROR) << "❌ modbus_led_find init_flag error or data is null";

        return YJ_ERROR;
    }

    while (Pmodbus_led_rtu_master->read_holding_register_multiple(addr, 0x05, dataTorecv) < 0 && addr != 0x00)
    {
        LOG(ERROR) << "❌ modbus_led_find error addr: 0x" << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)addr;
        addr++;
    }

    if (addr == 0x00)
    {
        LOG(ERROR) << "❌ modbus_led_find error not found";
        return YJ_ERROR;
    }
    else
    {
        LOG(INFO) << "✅ modbus_led_find success addr: 0x" << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)addr;
        return YJ_OK;
    }
}

yj_return modbus_led_read_all_status(modbus_led_data_t *data)
{
    std::vector<uint16_t> dataTorecv(2);

    if (init_flag == YJ_ERROR || !data)
    {
        LOG(ERROR) << "❌ modbus_led_read_all init_flag error or data is null";

        return YJ_ERROR;
    }

    if (Pmodbus_led_rtu_master->read_holding_register_multiple(MODBUS_LED_MODBUS_SLAVE_ADDR, 0x05, dataTorecv) < 0)
    {
        LOG(ERROR) << "❌ modbus_led_read_all read holding regs error";
        return YJ_ERROR;
    }

    data->beep_status = dataTorecv[0];
    data->led_status = dataTorecv[1];

    return YJ_OK;
}

yj_return modbus_led_operate_beep(modbus_beep_status_en status)
{
    std::vector<uint16_t> dataTorecv(1);
    dataTorecv[0] = status;

    if (init_flag == YJ_ERROR)
    {
        LOG(ERROR) << "❌ modbus_led_operate_beep init_flag error or data is null";

        return YJ_ERROR;
    }
    if (Pmodbus_led_rtu_master->write_holding_register_single(MODBUS_LED_MODBUS_SLAVE_ADDR, 0x05, dataTorecv) < 0)
    {
        LOG(ERROR) << "❌ modbus_led_operate_beep write holding regs error";
        return YJ_ERROR;
    }
    return YJ_OK;
}

yj_return modbus_led_operate_led(modbus_led_status_en status)
{
    std::vector<uint16_t> dataTorecv(1);
    dataTorecv[0] = status;

    if (init_flag == YJ_ERROR)
    {
        LOG(ERROR) << "❌ modbus_led_operate_led init_flag error or data is null";

        return YJ_ERROR;
    }
    if (Pmodbus_led_rtu_master->write_holding_register_single(MODBUS_LED_MODBUS_SLAVE_ADDR, 0x06, dataTorecv) < 0)
    {
        LOG(ERROR) << "❌ modbus_led_operate_led write holding regs error";
        return YJ_ERROR;
    }
    return YJ_OK;
}

void modbus_led_print_data(modbus_led_data_t *data)
{
    if (!data)
    {
        LOG(ERROR) << "modbus_led_print_data data is null";
        return;
    }
    LOG(INFO) << "============= data start =============";
    switch (data->beep_status)
    {
    case MODBUS_LED_BEEP_OFF:
        LOG(INFO) << "beep status: off";
        break;
    case MODBUS_LED_BEEP_ON:
        LOG(INFO) << "beep status: on";
        break;
    default:
        LOG(ERROR) << "beep status: error";
        break;
    }

    switch (data->led_status)
    {
    case MODBUS_LED_ALL_OFF:
        LOG(INFO) << "led status: all off";
        break;
    case MODBUS_LED_RED_ON:
        LOG(INFO) << "led status: red on";
        break;
    case MODBUS_LED_RED_BLINK_SLOW:
        LOG(INFO) << "led status: red blink slow";
        break;
    case MODBUS_LED_RED_BLINK_FAST:
        LOG(INFO) << "led status: red blink fast";
        break;
    case MODBUS_LED_GREEN_ON:
        LOG(INFO) << "led status: green on";
        break;
    case MODBUS_LED_GREEN_BLINK_SLOW:

        LOG(INFO) << "led status: green blink slow";
        break;
    case MODBUS_LED_GREEN_BLINK_FAST:
        LOG(INFO) << "led status: green blink fast";
        break;
    case MODBUS_LED_BLUE_ON:
        LOG(INFO) << "led status: blue on";
        break;
    case MODBUS_LED_BLUE_BLINK_SLOW:
        LOG(INFO) << "led status: blue blink slow";
        break;
    case MODBUS_LED_BLUE_BLINK_FAST:
        LOG(INFO) << "led status: blue blink fast";
        break;
    case MODBUS_LED_YELLOW_ON:
        LOG(INFO) << "led status: yellow on";
        break;
    case MODBUS_LED_YELLOW_BLINK_SLOW:
        LOG(INFO) << "led status: yellow blink slow";
        break;
    case MODBUS_LED_YELLOW_BLINK_FAST:
        LOG(INFO) << "led status: yellow blink fast";
        break;
    default:
        LOG(ERROR) << "led status: error";
        break;
    }
    // LOG(INFO) << "beep status value: 0x"<< std::hex << std::setw(4) << std::setfill('0') << (int)data->beep_status;
    // LOG(INFO) << "led status value: 0x"<< std::hex << std::setw(4) << std::setfill('0') << (int)data->led_status;

    LOG(INFO) << "============= data end =============";
}